﻿//Array Generator
namespace Diplom.Utilities
{
    using System;
    using System.Globalization;
    using System.IO;
    using Diplom.NumericArrays;

    public enum Function{
        Noise = 0,
        Sin = 1,
        Cos = 2,
        Ln = 3,
        Exp = 4,
        Dirac = 5,
        Heaviside = 6,
        File = 7,
        CorrelationNoise = 8
    }

    public static class ArrayGenerator
    {
        /// <summary>
        /// Generator of numeric arrays
        /// </summary>        
        public static void Generate(Function func, ref Numeric1DArray array, Parameters parameter)
        {
            switch (func)
            {
                case Function.Cos:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        double step = (parameter.End - parameter.Begin) / parameter.Length;

                        double curX = parameter.Begin;
                        for (int i = 0; i < parameter.Length; i++, curX += step)
                        {
                            array.Insert(i, parameter.Amplitude * Math.Cos(parameter.Frequency * curX - parameter.Phase));
                        }
                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                
                case Function.Dirac:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        for (int i=0; i<parameter.Length; i++)
                        {
                            if (i != (parameter.Length + 1) / 2)
                            {
                                array.Insert(i++, 0);
                            }
                            else
                            {
                                array.Insert(i++, 1);
                            }
                        }
                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.Exp:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        double step = (parameter.End - parameter.Begin) / parameter.Length;
                        
                        double curX = parameter.Begin;
                        for (int i = 0; i < parameter.Length; i++, curX += step)
                        {
                            array.Insert(i, Math.Exp(-curX));
                        }

                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.File:
                    {
                        throw new Exception("Unsupported command");

                    }
                case Function.Heaviside:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        for (int i = 0; i < parameter.Length; i++)
                        {
                            if (i < (parameter.Length + 1) / 2)
                            {
                                array.Insert(i, 0);
                            }
                            else
                            {
                                array.Insert(i, 1);
                            }
                        }
                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.Ln:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        double step = (parameter.End - parameter.Begin) / parameter.Length;
                        
#warning Нужно проверять интервалы которуе подают, тут например > 0 должно быть
                        double curX = parameter.Begin;
                        for (int i = 0; i < parameter.Length; i++, curX += step)
                        {
                            array.Insert(i, parameter.Amplitude * Math.Log(parameter.Frequency * curX - parameter.Phase));
                        }

                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.Noise:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        Random rnd = new Random(DateTime.Now.Millisecond);
                        for (int i = 0; i < array.Length; i++)
                        {
                            array.Insert(i, parameter.Amplitude * (rnd.NextDouble() * 2 - 1.0));
                        }
                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.CorrelationNoise:
                    {
                        Numeric1DArray array2 = new Numeric1DArray(parameter.Length);
                        Random rnd = new Random(DateTime.Now.Millisecond);
                        for (int i = 0; i < array2.Length; i++)
                        {
                            array2.Insert(i, parameter.Amplitude * (rnd.NextDouble() * 2 - 1.0));
                        }
                        array = new Numeric1DArray(parameter.Length);
                        for (int i = 0; i < array2.Length; i++)
                        {
                            double val = array2.Items[i];
                            if (i > 0) val += 0.5 * array2.Items[i - 1];
                            if (i > 1) val += 0.25 * array2.Items[i - 2];
                            if (i + 1 < array2.Length) val += 0.5 * array2.Items[i + 1];
                            if (i + 2 < array2.Length) val += 0.25 * array2.Items[i + 2];
                            array.Insert(i, val);
                        }
                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;
                case Function.Sin:
                    {
                        array = new Numeric1DArray(parameter.Length);
                        double step = (parameter.End - parameter.Begin) / parameter.Length;
                        
                        double curX = parameter.Begin;
                        for (int i = 0; i < parameter.Length; i++, curX += step)
                        {
                            array.Insert(i, parameter.Amplitude * Math.Sin(parameter.Frequency * curX - parameter.Phase));
                        }

                        array.TimeMin = 1;// parameter.Begin;
                        array.TimeMax = parameter.Length;// parameter.End;
                        array.InitCharacters();

                    } break;                
                default:
                    {
                        throw new Exception("Unsupported command");

                    }
            }
        }

        /// <summary>
        /// Read numeric array from file
        /// </summary>
        public static void Generate(String path, ref Numeric1DArray array)
        {
            Stream stream = null;
            StreamReader sReader = null;
            System.Globalization.CultureInfo culture = null;

            // чтобы не было проблем с разделителями (точки/запятые, в разных культурах поразному)
            // в прошлой версии это было, видимо столкнулся с такой проблемой
            culture = (CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

            try
            {
                stream = File.Open(path, FileMode.Open);
                sReader = new StreamReader(stream);

                String str;
                Int32 length;
                str = sReader.ReadLine();
                try
                {
                    Int32.TryParse(str, out length);
                    array = new Numeric1DArray(length);
                }
                catch(Exception ex)
                {
                    throw ex;
                }

                try
                {
                    double item;
                    for (int i = 0; i < length; i++)
                    {
                        str = sReader.ReadLine();
                        Double.TryParse(str, out item);
                        array.Insert(i, item);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }

                array.InitCharacters();
            }
            catch (Exception ex)
            {
                throw new Exception("Error in loading file", ex);
            }
            finally
            {
                if (sReader != null)
                {
                    sReader.Close();
                    sReader.Dispose();
                }

                if (stream != null)
                {
                    stream.Close();
                    stream.Dispose();
                }

                //возвращаем предыдущую культуру на место!
                System.Threading.Thread.CurrentThread.CurrentCulture = (CultureInfo)culture.Clone();
                culture = null;
            }
        }

        /// <summary>
        /// Sum of numeric arrays
        /// </summary>        
        public static void SumArrays(ref Numeric1DArray a, ref Numeric1DArray b, int startindex)
        {
            for (int i = startindex; i < Math.Min(startindex + b.Length, a.Length); i++)
            {
                a.Items[i] += b.Items[i - startindex];
            }
            a.InitCharacters();
        }

        /// <summary>
        /// Difference of numeric arrays
        /// </summary>        
        public static void DiffArrays(ref Numeric1DArray a, ref Numeric1DArray b, int startindex)
        {
            for (int i = startindex; i < Math.Min(startindex + b.Length, a.Length); i++)
            {
                a.Items[i] -= b.Items[i - startindex];
            }
            a.InitCharacters();
        }

        /// <summary>
        /// Multiplication of numeric arrays
        /// </summary>        
        public static void MultArrays(ref Numeric1DArray a, ref Numeric1DArray b, int startindex)
        {
            for (int i = startindex; i < Math.Min(startindex + b.Length, a.Length); i++)
            {
                a.Items[i] *= b.Items[i - startindex];
            }
            a.InitCharacters();
        }

        public static void SaveArray(ref Numeric1DArray a, string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new Exception("path is empty");
            }
            Stream stream = null;
            StreamWriter sw = null;

            System.Threading.Thread.CurrentThread.CurrentCulture =
                System.Globalization.CultureInfo.InvariantCulture;
            try
            {
                stream = File.Open(path, FileMode.Create);
                sw = new StreamWriter(stream);

                sw.WriteLine(a.Length);
                for (int i = 0; i < a.Length; i++)
                {
                    sw.WriteLine(a.Items[i]);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sw != null)
                {
                    sw.Close();
                    sw.Dispose();
                }
                if (stream != null)
                {
                    stream.Close();
                    stream.Dispose();
                }
            }

        }
    }
}
